/*
   DOES NOT USE THE BUILT-IN DETECTION FUNCTIONALITY!!
     
alert tcp $EXTERNAL_NET $HTTP_PORTS -> $HOME_NET any (msg:"WEB-CLIENT Microsoft Excel malformed MergeCells record exploit attempt"; flowbits:isset,file.xls; flow:to_client,established; content:"|00 02 0E 00|"; content:"|00 00|"; distance:12; within:2; metadata:policy balanced-ips drop, policy security-ips drop, service http; reference:cve,2010-3237; reference:url,www.microsoft.com/technet/security/bulletin/MS10-080.mspx; classtype:attempted-user; sid:17761;)
*/   
/*
 * Vuln Title: Microsoft Excel malformed MergeCells record remote code execution 
 *
 * Copyright (C) 2005-2010 Sourcefire, Inc. All Rights Reserved
 *
 * Written by Sojeong Hong, Sourcefire VRT <shong@sourcefire.com>
 *
 * Auto-generated by shong
 *
 * This file may contain proprietary rules that were created, tested and
 * certified by Sourcefire, Inc. (the "VRT Certified Rules") as well as
 * rules that were created by Sourcefire and other third parties and
 * distributed under the GNU General Public License (the "GPL Rules").  The
 * VRT Certified Rules contained in this file are the property of
 * Sourcefire, Inc. Copyright 2005 Sourcefire, Inc. All Rights Reserved.
 * The GPL Rules created by Sourcefire, Inc. are the property of
 * Sourcefire, Inc. Copyright 2002-2005 Sourcefire, Inc. All Rights
 * Reserved.  All other GPL Rules are owned and copyrighted by their
 * respective owners (please see www.snort.org/contributors for a list of
 * owners and their respective copyrights).  In order to determine what
 * rules are VRT Certified Rules or GPL Rules, please refer to the VRT
 * Certified Rules License Agreement.
 */

#include "sf_snort_plugin_api.h"
#include "sf_snort_packet.h"

//#define DEBUG 1
#ifdef DEBUG
#define DEBUG_SO(code) code
#else
#define DEBUG_SO(code)
#endif

#ifndef READ_LITTLE_16
#define READ_LITTLE_16(p) (*((uint8_t *)(p) + 1) << 8) \
                | (*(p))
#endif

/* declare detection functions */
int rule17761eval(void *p);

static ContentInfo rule17761content_deleted =
{
    (uint8_t *) VRT_RAND_STRING, /* pattern that should not invoke detection */
    0, /* depth */
    0, /* offset */
    CONTENT_FAST_PATTERN, /* flags */
    NULL, /* holder for boyer/moore PTR */
    NULL, /* more holder info - byteform */
    0, /* byteform length */
    0 /* increment length*/
};

static RuleOption rule17761deleted =
{
    OPTION_TYPE_CONTENT,
    {
        &rule17761content_deleted
    }
};

#if 0
/* declare rule data structures */
/* flowbits:isset "file.xls"; */
static FlowBitsInfo rule17761flowbits0 =
{
    "file.xls",
    FLOWBIT_ISSET,
    0,
};

static RuleOption rule17761option0 =
{
    OPTION_TYPE_FLOWBIT,
    {
        &rule17761flowbits0
    }
};
/* flow:established, to_client; */
static FlowFlags rule17761flow1 = 
{
    FLOW_ESTABLISHED|FLOW_TO_CLIENT
};

static RuleOption rule17761option1 =
{
    OPTION_TYPE_FLOWFLAGS,
    {
        &rule17761flow1
    }
};
// content:"|00 02 0E 00|", depth 0, fast_pattern; 
static ContentInfo rule17761content2 = 
{
    (uint8_t *) "|00 02 0E 00|", /* pattern (now in snort content format) */
    0, /* depth */
    0, /* offset */
    CONTENT_RELATIVE|CONTENT_FAST_PATTERN|CONTENT_BUF_NORMALIZED, /* flags */
    NULL, /* holder for boyer/moore PTR */
    NULL, /* more holder info - byteform */
    0, /* byteform length */
    0 /* increment length*/
};

static RuleOption rule17761option2 = 
{
    OPTION_TYPE_CONTENT,
    {
        &rule17761content2
    }
};
// content:"|00 00|", offset 12, depth 2, relative; 
static ContentInfo rule17761content3 = 
{
    (uint8_t *) "|00 00|", /* pattern (now in snort content format) */
    2, /* depth */
    12, /* offset */
    CONTENT_RELATIVE|CONTENT_BUF_NORMALIZED, /* flags */
    NULL, /* holder for boyer/moore PTR */
    NULL, /* more holder info - byteform */
    0, /* byteform length */
    0 /* increment length*/
};

static RuleOption rule17761option3 = 
{
    OPTION_TYPE_CONTENT,
    {
        &rule17761content3
    }
};
#endif

/* references for sid 17761 */
/* reference: cve "2010-3237"; */
static RuleReference rule17761ref1 = 
{
    "cve", /* type */
    "2010-3237" /* value */
};

/* reference: url "www.microsoft.com/technet/security/bulletin/MS10-080.mspx"; */
static RuleReference rule17761ref2 = 
{
    "url", /* type */
    "www.microsoft.com/technet/security/bulletin/MS10-080.mspx" /* value */
};

static RuleReference *rule17761refs[] =
{
    &rule17761ref1,
    &rule17761ref2,
    NULL
};
/* metadata for sid 17761 */
/* metadata:service http, policy balanced-ips drop, policy security-ips drop; */
//static RuleMetaData rule17761service1 = 
//{
//    "service http"
//};
//
//
//static RuleMetaData rule17761policy1 = 
//{
//    "policy balanced-ips drop"
//};
//
//static RuleMetaData rule17761policy2 = 
//{
//    "policy security-ips drop"
//};


static RuleMetaData *rule17761metadata[] =
{
//    &rule17761service1,
//    &rule17761policy1,
//    &rule17761policy2,
    NULL
};

RuleOption *rule17761options[] =
{
    &rule17761deleted,
//    &rule17761option0,
//    &rule17761option1,
//    &rule17761option2,
//    &rule17761option3,
    NULL
};

Rule rule17761 = {
   /* rule header, akin to => tcp any any -> any any */
   {
       IPPROTO_TCP, /* proto */
       "$EXTERNAL_NET", /* SRCIP     */
       "$HTTP_PORTS", /* SRCPORT   */
       0, /* DIRECTION */
       "$HOME_NET", /* DSTIP     */
       "any", /* DSTPORT   */
   },
   /* metadata */
   { 
       3,  /* genid */
       17761, /* sigid */
       5, /* revision */
       "attempted-user", /* classification */
       0,  /* hardcoded priority XXX NOT PROVIDED BY GRAMMAR YET! */
       "DELETED WEB-CLIENT Microsoft Excel malformed MergeCells record exploit attempt",     /* message */
       rule17761refs /* ptr to references */
       ,rule17761metadata
   },
   rule17761options, /* ptr to rule options */
   &rule17761eval, /* use the built in detection function */
   1 //0 /* am I initialized yet? */
};


/* detection functions */
// #if 0 // Don't compile the detection functions if they're not used
int rule17761eval(void *p) {

    return RULE_NOMATCH;

#if 0
    const uint8_t *cursor_normal = 0;    
    SFSnortPacket *sp = (SFSnortPacket *) p;

    const uint8_t *end_of_payload;
    const uint8_t *cursor_record, *cursor_mergecells, *end_of_mergecells;
    
    uint16_t Dimensions_colMac;  
    uint16_t MergeCells_cmcs, MergeCells_colFirst, MergeCells_colLast; 
    uint16_t record_size, record_type;

    uint8_t loopcounter = 0;

    DEBUG_SO(printf("rule17761eval enter\n");)
        
    if(sp == NULL)
        return RULE_NOMATCH;

    // flowbits:isset "file.xls";
    if (processFlowbits(p, rule17761options[0]->option_u.flowBit) <= 0) 
        return RULE_NOMATCH;
            
    // flow:established, to_client;
    if (checkFlow(p, rule17761options[1]->option_u.flowFlags) <= 0)
        return RULE_NOMATCH;

    if(getBuffer(sp, CONTENT_BUF_NORMALIZED, &cursor_normal, &end_of_payload) <= 0)
        return RULE_NOMATCH;

    // Dimensions's record header
    // content:"|00 02 0E 00|", depth 0, fast_pattern, relative;
    while (contentMatch(p, rule17761options[2]->option_u.content, &cursor_normal) > 0) {

        // DoS protection
        loopcounter++;
        if(loopcounter > 10)
           return RULE_NOMATCH;
         
        // Dimensions.reserved
        // content:"|00 00|", offset 12, depth 2, relative;
        if (contentMatch(p, rule17761options[3]->option_u.content, &cursor_normal) > 0) {

            DEBUG_SO(printf("Dimensions record found\n");)
                     
            cursor_record = cursor_normal - 4;
            Dimensions_colMac = READ_LITTLE_16(cursor_record);
            DEBUG_SO(printf("Dimensions.colMac [0x%04x]\n", Dimensions_colMac);)
                
            cursor_record = cursor_normal;
            
            /*
               We want to search for the MergeCells record by skipping subsequent records
               (using the record's size) rather than using content:"|E5 00|"; distance:0,
               which is not unique enough and we might end up with looking at wrong data
            */
               
            while (cursor_record + 14 <= end_of_payload)
            {
                record_type = READ_LITTLE_16(cursor_record);
                cursor_record += 2;

                record_size = READ_LITTLE_16(cursor_record);
                cursor_record += 2;
                   
                if (record_type == 0xE5)  // MergeCells record
                {
                    cursor_mergecells = cursor_record;
                        
                    MergeCells_cmcs = READ_LITTLE_16(cursor_mergecells);
                    cursor_mergecells += 6; // skip MergeCells.cmcs, MergeCells.rgref[0].rwFirst, MergeCells.rgref[0].rwLast

                    // Check changed to 300 to defend against DoS
                    if (MergeCells_cmcs > 100 /*1026*/)  // either cmcs value is wrong or we are looking at a wrong record boundary
                        break;

                    // precompute the end of the mergecells to speed up the loop
                    end_of_mergecells = cursor_record + 2 + (MergeCells_cmcs * 8);
                    if(end_of_mergecells > end_of_payload || (cursor_record > end_of_mergecells))
                        end_of_mergecells = end_of_payload;

                    //for (i = 0; (i < MergeCells_cmcs) && (cursor_mergecells + 4 <= end_of_payload); i++)
                    while (cursor_mergecells + 4 < end_of_mergecells)
                    {
                        MergeCells_colFirst = READ_LITTLE_16(cursor_mergecells);
                        DEBUG_SO(printf("MergeCells.rgref.colFirst [0x%04x]\n", MergeCells_colFirst);)                            
                        cursor_mergecells += 2;

                        if (MergeCells_colFirst > Dimensions_colMac)
                            return RULE_MATCH;
                        
                        MergeCells_colLast = READ_LITTLE_16(cursor_mergecells);
                        DEBUG_SO(printf("MergeCells.rgref.colLast [0x%04x]\n", MergeCells_colLast);)
                        cursor_mergecells += 6; // skip MergeCells.rgref[i].colLast, MergeCells.rgref[i+1].rwFirst, MergeCells.rgref[i+1].rwLast

                        if (MergeCells_colLast > Dimensions_colMac)
                            return RULE_MATCH;

                    }                    
                }
                
                cursor_record += record_size;
            }
        }
    }
    
    return RULE_NOMATCH;
#endif

}
// #endif // 0 Don't compile the detection functions if they're not used
/*
Rule *rules[] = {
    &rule17761,
    NULL
};
*/
